package com.example.administrator.myapplication.bus;

import android.hardware.camera2.DngCreator;
import android.text.TextUtils;
import android.view.SubMenu;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DNBus {

    private DNBus() {
    }

    private volatile static DNBus instance;
    private Map<Class, List<SubscribeMethod>> methodCache = new HashMap<>();
    private Map<String,List<Subscribetion>> lableCache = new HashMap<>();

    public static DNBus getInstance() {
        if (instance == null) {
            synchronized (DNBus.class) {
                if (instance == null) {
                    instance = new DNBus();
                }
            }
        }
        return instance;
    }

    public void register(Object obj) {
        List<SubscribeMethod> subscribeMethods = findSubscribeMethod(obj.getClass());
        for (SubscribeMethod method : subscribeMethods) {
            String lable = method.getLable();
            List<Subscribetion> subscribetions = lableCache.get(lable);
            if (subscribetions == null) {
                subscribetions = new ArrayList<>();
                lableCache.put(lable,subscribetions);
                subscribetions.add(new Subscribetion(method,obj));
            }else {
                subscribetions.add(new Subscribetion(method,obj));
            }
        }
    }

    public void post(String lable,Object ... params){
        List<Subscribetion> subscribetions = lableCache.get(lable);
        if (subscribetions == null) {
            return;
        }
        for (Subscribetion subscribetion : subscribetions) {
            SubscribeMethod subscribeMethod = subscribetion.getSubscribeMethod();
            Object object = subscribetion.getObject();
            Class[] paramClasses = subscribeMethod.getParamClasses();
            Object[] realParams = new Object[paramClasses.length];
            if (params != null) {
                for (int i = 0; i < paramClasses.length; i++) {
                    if (i<params.length && TextUtils.equals(paramClasses[i].getName(),
                            params[i].getClass().getName())){
                        realParams[i] = params[i];
                    }else {
                        realParams[i] = null;
                    }
                }
            }
            try {
                subscribeMethod.getMethod().invoke(object,realParams);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }

    }

    private List<SubscribeMethod> findSubscribeMethod(Class subscriber) {
        List<SubscribeMethod> subscribeMethods = methodCache.get(subscriber);
        if (subscribeMethods == null) {
            subscribeMethods = new ArrayList<>();
            Method[] declaredMethods = subscriber.getDeclaredMethods();
            for (Method method : declaredMethods) {
                Subscribe annotation = method.getAnnotation(Subscribe.class);
                if (annotation == null) {
                    continue;
                }
                String[] values = annotation.value();
                for (String value : values) {
                    method.setAccessible(true);
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    for (int i = 0; i < parameterTypes.length; i++) {
                        if (parameterTypes[i].getSimpleName().equals("int")){
                            parameterTypes[i] = Integer.class;
                        }
                    }
                    SubscribeMethod subscribeMethod = new SubscribeMethod();
                    subscribeMethod.setParamClasses(parameterTypes);
                    subscribeMethod.setMethod(method);
                    subscribeMethod.setLable(value);
                    subscribeMethods.add(subscribeMethod);
                }
            }
            methodCache.put(subscriber, subscribeMethods);
            return subscribeMethods;
        } else {
            return subscribeMethods;
        }
    }
}
